home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / Toolbox / BigScrolling / Big Scrolling.p next >
Encoding:
Text File  |  1993-01-15  |  10.1 KB  |  269 lines  |  [TEXT/PJMM]

  1. { Big Scrolling Pascal unit }
  2.  
  3. unit BigScrolling;
  4.  
  5. interface
  6. uses
  7.     Traps;
  8.  
  9. type
  10.  
  11. { We attach this record, which contains longint values for the minimum, maximum and value of the control, to the }
  12. { control's refcon.  Our "MyGetCtlValue" and "MySetCtlValue" look for a pointer to this record in the control's }
  13. { refcon so we can find it. }
  14.  
  15.     bigValues = record
  16.             bigMin: longint;
  17.             bigMax: longint;
  18.             bigValue: longint;
  19.         end;
  20.     bigValuesPtr = ^bigValues;
  21.  
  22. procedure InitializeApplication;
  23. procedure TerminateApplication;
  24. procedure CloseAppWindow (theWindow: WindowPtr);
  25. procedure DrawWindow (theWindow: WindowPtr; drawingPort: grafPtr; printing: BOOLEAN; isActive: BOOLEAN);
  26. procedure DoContentClick (window: WindowPtr; event: EventRecord);
  27. procedure MySetCtlValue (theControl: ControlHandle; theValue: longint);
  28. function MyGetCtlValue (theControl: ControlHandle): longint;
  29. procedure FixMyCtlValue (theControl: ControlHandle);
  30.  
  31. implementation
  32.  
  33. var
  34.     ourScrollBar: ControlHandle;
  35.  
  36. { MySetCtlValue takes a longint control value.  It stuffs the value into the bigValues record pointed to by the }
  37. { control refcon and normalizes it into a 0..32767 range for the control's actual value (signed integer).  This}
  38. { is obviously a loss of precision, but it's OK because this is only used for displaying the thumb position by}
  39. { the Control Manager. }
  40.  
  41. procedure MySetCtlValue (theControl: ControlHandle; theValue: longint);
  42.  
  43.     var
  44.         extendedValue: extended;
  45.         intValue: integer;
  46.         ourValues: bigValuesPtr;
  47.  
  48.  
  49.     begin
  50.         ourValues := bigValuesPtr(GetCRefCon(theControl));
  51.         with ourValues^ do
  52.             begin
  53.  
  54. { We have to pin the value to our minimum and maximum values so we don't underflow or overflow. }
  55.  
  56.                 if theValue > bigMax then
  57.                     bigValue := bigMax
  58.                 else if theValue < bigMin then
  59.                     bigValue := bigMin
  60.                 else
  61.                     bigValue := theValue;
  62.  
  63. { To normalize, we subtract the bigMin from bigMax to get the control's range of values.  Dividing that interval}
  64. { by 32767 tells us how much our longint control value has to change before the real control value changes by one. }
  65. { For example, if our range is 0 to 65535, the real control value moves by one every time our value changes}
  66. { by two.  Once we have that interval, we divide our real value by it to get a normalized value. }
  67. { (our Value)/(interval / 32767) is the same as (ourValue * 32767) / interval, which is the form we use.}
  68. { 32767 is expressed as 32767.0 so extended calculations are done, to avoid overflow even for really big}
  69. { values. }
  70.  
  71.                 extendedValue := (((bigValue - bigMin) * 32767.0) / (bigMax - bigMin));
  72.                                                                                         { this will always be between 0 and 32767 }
  73.                 intValue := round(extendedValue);                                            { explicitly truncate to integer }
  74.                 SetCtlValue(theControl, intValue);
  75.             end;    {with}
  76.     end;
  77.  
  78. { MyGetCtlValue returns our longint value from the bigValues record attached to the refcon.  There's no calculation}
  79. { to do here, because MySetCtlValue and FixMyCtlValue does all that for us. }
  80.  
  81. function MyGetCtlValue (theControl: ControlHandle): longint;
  82.  
  83.     var
  84.         ourValues: bigValuesPtr;
  85.  
  86.     begin
  87.         ourValues := bigValuesPtr(GetCRefCon(theControl));
  88.         MyGetCtlValue := ourValues^.bigValue;
  89.     end;
  90.  
  91.  
  92. { FixMyCtlValue resets bigValue to something resembling the actual control value, for those occasions when the }
  93. { Control Manager drags the thumb for you and resets the value based on the min and max fields in the control }
  94. { record.   It reverses the MySetCtlValue calculation to get an approximation of where the big control value is for }
  95. { the place where the user dragged the thumb; this is the best we can do because the scroll bar is never going to get}
  96. { the resolution of the values (you can't have a scroll bar one million pixels tall).  In the special case that they're at }
  97. { the very bottom of the scroll bar, we set the value to bigMax so it's more consistent with what users expect. }
  98. { Since each step in the real control value represents many steps in ours, if the new control value is the same as }
  99. { where the old bigValue would put it, we don't change it.  This means if you click on the thumb but don't move it, }
  100. { the control value doesn't change. We have to use extended arithmetic here as well to avoid round-off errors.}
  101.  
  102. procedure FixMyCtlValue (theControl: ControlHandle);
  103.  
  104.     var
  105.         allegedValue, intValue: integer;
  106.         oldBigValue: longint;
  107.         ourValues: bigValuesPtr;
  108.         extendedValue, newBigExtended: extended;
  109.  
  110.     begin
  111.         ourValues := bigValuesPtr(GetCRefCon(theControl));
  112.         allegedValue := GetCtlValue(theControl);
  113.         with ourValues^ do
  114.             begin
  115.                 oldBigValue := bigValue;
  116. { To reverse the calculation, we divide the interval of possible values by the maximum real control value, then multiply }
  117. { that by the value the Control Manager has.  Since the minimum might not be zero, we add it in as well. }
  118.  
  119.                 newBigExtended := (((((bigMax - bigMin) / 32767.0)) * allegedValue) + bigMin);
  120.                 bigValue := round(newBigExtended);
  121.  
  122.                         { Now, if that new bigValue has the same CtlValue as the old bigValue, restore the old one. }
  123.  
  124.                 extendedValue := (((oldBigValue - bigMin) * 32767.0) / (bigMax - bigMin));
  125.                                                                                         { this will always be between 0 and 32767 }
  126.                 intValue := round(extendedValue);                                            { explicitly truncate to integer }
  127.                 if intValue = allegedValue then
  128.                     bigValue := oldBigValue
  129.                 else if allegedValue = 32767 then
  130.                     bigValue := bigMax                                        { pin to bottom only if not changing value otherwise }
  131.  
  132.             end;   { with }
  133.     end;              { procedure }
  134.  
  135. procedure InitializeApplication;
  136.     var
  137.         theWindow: WindowPtr;
  138.         newValuesPtr: bigValuesPtr;
  139.  
  140.     begin
  141.  
  142. { Create and show our window }
  143.  
  144.         theWindow := GetNewWindow(128, nil, Pointer(-1));        { window is invisible in the WIND resource }
  145.         ourScrollBar := GetNewControl(128, theWindow);
  146.         newValuesPtr := bigValuesPtr(NewPtr(sizeof(bigValues)));
  147.         newValuesPtr^.bigMin := 0;                                                { an arbitrary minimum }
  148.         newValuesPtr^.bigMax := 1500000;                                    { an arbitrary maximum }
  149.         SetCRefCon(ourScrollBar, longint(newValuesPtr));            { put a pointer to the record in the control's refCon}
  150.         MySetCtlValue(ourScrollBar, 1000000);                            { an arbitrary initial value }
  151.         ShowWindow(theWindow);
  152.  
  153.     end;
  154.  
  155. procedure TerminateApplication;                                            { Called by Sample.p -- not needed in this unit }
  156.     begin
  157.     end;
  158.  
  159. procedure CloseAppWindow (theWindow: WindowPtr);
  160.  
  161.     begin
  162.         DisposePtr(Ptr(GetCRefCon(ourScrollBar)));                        { Dispose of our bigValues structure }
  163.         CloseWindow(theWindow);                                                    { and close the window. }
  164.     end;
  165.  
  166. procedure DrawTheValue (theValue: longint);
  167.  
  168.     var
  169.         tempString: Str255;
  170.         myRect: Rect;
  171.     begin
  172.         SetRect(myRect, 40, 40, 150, 100);                                { an arbitrary sized rectangle to draw in }
  173.         EraseRect(myRect);
  174.         MoveTo(50, 50);
  175.         TextFont(geneva);
  176.         NumToString(theValue, tempString);                                    { turn the value into a string }
  177.         DrawString(tempString);
  178.     end;
  179.  
  180. { This procedure is called by the shell-like Sample.p file to draw windows.  Sample.p sets "printing" to TRUE if }
  181. { it's printing (though this snippet doesn't print), so we ignore that.  We also ignore "isActive" because in this }
  182. { program, we have one window and it's always active, and we ignore "drawingPort" because it's NIL unless we're }
  183. { printing, in which case it's the printing grafPort.  All we have to do in this routine is call DrawControls to draw}
  184. { our scroll bar, then call DrawTheValue to provide an integer representation of it. }
  185.  
  186. procedure DrawWindow (theWindow: WindowPtr; drawingPort: grafPtr; printing: boolean; isActive: boolean);
  187.     var
  188.         oldPort: grafPtr;
  189.         theValue: longint;
  190.     begin
  191.         GetPort(oldPort);
  192.         SetPort(theWindow);
  193.         DrawControls(theWindow);
  194.         theValue := MyGetCtlValue(ourScrollBar);
  195.         DrawTheValue(theValue);
  196.         SetPort(oldPort);
  197.     end;
  198.  
  199. { NonThumbAction is the action routine we pass to TrackControl when the user clicks on the scroll arrows or page}
  200. { region of the scroll bar.  We add arbitrary values to the control value, then call MySetCtlValue to change the }
  201. { control's value and redraw it.  We then draw the value after retrieving it again -- we retrieve it with MyGetCtlValue }
  202. { just in case our last change would have gone under the minimum or over the maximum.  MySetCtlValue prevents that, }
  203. { and we get the corrected value before displaying it. }
  204.  
  205. procedure NonThumbAction (theControl: ControlHandle; partCode: integer);
  206.  
  207.     var
  208.         ourValue: longint;
  209.  
  210.     const
  211.         arrowUpAmount = -1;
  212.         arrowDownAmount = 1;
  213.         pageUpAmount = -1000;
  214.         pageDownAmount = 1000;
  215.  
  216.     begin
  217.         ourValue := MyGetCtlValue(theControl);
  218.  
  219.         case partCode of
  220.             inUpButton: 
  221.                 ourValue := ourValue + arrowUpAmount;
  222.             inDownButton: 
  223.                 ourValue := ourValue + arrowDownAmount;
  224.             inPageUp: 
  225.                 ourValue := ourValue + pageUpAmount;
  226.             inPageDown: 
  227.                 ourValue := ourValue + pageDownAmount;
  228.         end;
  229.         MySetCtlValue(theControl, ourValue);
  230.         DrawTheValue(MyGetCtlValue(theControl));            { in case it got pinned to the minimum or maximum }
  231.     end;
  232.  
  233. { Sample.p calls DoContentClick when it finds a click in the content region of an app window (that's us).  Since all }
  234. { we care about is the scroll bar, we call FindControl and then TrackControl, passing NonThumbAction if they click}
  235. { in the scroll bar (but not in the thumb), and passing NIL if they click in the thumb.  If they dragged the thumb, we}
  236. { use FixMyCtlValue to repair the bigValue in our private record on the refcon. }
  237.  
  238. procedure DoContentClick (window: WindowPtr; event: EventRecord);
  239.     var
  240.         thePartCode, theNewPartCode: integer;
  241.         ourLocalPoint: Point;
  242.         ourControl: ControlHandle;
  243.         oldPort: grafPtr;
  244.  
  245.     begin
  246.         GetPort(oldPort);
  247.         SetPort(window);
  248.         ourLocalPoint := event.where;
  249.         GlobalToLocal(ourLocalPoint);
  250.         thePartCode := FindControl(ourLocalPoint, window, ourControl);
  251.         case thePartCode of
  252.             0: 
  253.                 ;
  254.                      { we get and ignore zero if they mouse up outside the part they mouse down-ed in }
  255.             inUpButton, inDownButton, inPageUp, inPageDown: 
  256.                 theNewPartCode := TrackControl(ourControl, ourLocalPoint, @NonThumbAction);
  257.             inThumb: 
  258.                 begin
  259.                     theNewPartCode := TrackControl(ourControl, ourLocalPoint, nil);
  260.                     FixMyCtlValue(ourControl);                { change bigValue to match to where they moved the thumb }
  261.                     DrawTheValue(MyGetCtlValue(ourControl));
  262.                 end;
  263.         end;                { case thePartCode of }
  264.         SetPort(oldPort);
  265.  
  266.     end;
  267.  
  268.  
  269. end.